import matplotlib; matplotlib.use('Agg')
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier  # 导入决策树分类器
import numpy as np
import pandas as pd
from sklearn.metrics import RocCurveDisplay
from sklearn.decomposition import PCA
from matplotlib import pyplot as plt
from mlxtend.plotting import plot_decision_regions
from sklearn.tree import plot_tree
from joblib import dump
import pymysql
from flask import Flask, request, jsonify
from flask_cors import CORS
import base64
import threading
from io import BytesIO

# 创建全局绘图锁
plot_lock = threading.Lock()

# 生成示例数据
try:
    # 数据库配置
    db_config = {
        'host': 'localhost',
        'port': 3306,
        'user': 'root',
        'password': '1234',
        'database': 'cart_system',
        'charset': 'utf8mb4'
    }
    
    # 建立数据库连接
    connection = pymysql.connect(**db_config)
    
    # 执行SQL查询
    query = "SELECT SUM(osumprice) as '消费总量', SUM(osumprice) / COUNT(usrid) as '每次消费平均量', SUM(osumprice) >= 5000 as '标签' from `order` GROUP BY usrid ORDER BY SUM(osumprice)"
    X = pd.read_sql(query, connection)
    
    # 确保包含标签字段
    y = X['标签']
    X = X.drop('标签', axis=1)

    # 数据预处理
    X = X.fillna(0)  # 填充缺失值
    X = X.replace([np.inf, -np.inf], np.nan).fillna(0)  # 处理无穷大值
    # 验证数值有效性
    # 空值有效性验证
    if X.isnull().values.any():
        print("清洗前存在空值记录数量:", X.isnull().sum())
    
    # 过滤有效数据
    valid_mask = (X['消费总量'] >= 0) & (X['每次消费平均量'] >= 0)
    X = X[valid_mask].reset_index(drop=True)
    y = y[valid_mask].reset_index(drop=True)
    
    # 清洗后验证
    print("清洗后空值检查:\n", X.isnull().sum())
    print("X形状:", X.shape, "y形状:", y.shape)
    
except pymysql.Error as e:
    print(f"数据库连接失败: {e}")
    exit(1)

print(X)
print(y)
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建支持向量机模型
SVM_model = SVC(kernel='linear', C=0.1, random_state=42)
#创建对率回归模型
Loistic_model = LogisticRegression(C=0.1, random_state=42, max_iter=1000)
#创建决策树模型
DecisionTree_model = DecisionTreeClassifier(criterion='gini', max_depth=10, random_state=42)
# 最终数据完整性检查
print("训练前最终空值检查:\n", X_train.isnull().sum())

# 训练模型
SVM_model.fit(X_train, y_train)
Loistic_model.fit(X_train, y_train)
DecisionTree_model.fit(X_train, y_train)

#对率回归模型的预测概率
y_pred_prob_Loistic = Loistic_model.predict_proba(X)[:, 1]  # 使用测试集计算概率，避免在训练集上评估导致过度自信的预测
# 打印对率回归模型的预测概率
print("对率回归模型预测概率:")
for i, prob in enumerate(y_pred_prob_Loistic):
    print(f"样本{i+1}: {prob*100:.2f}%")

# 计算并打印支持向量机模型的决策函数值（类似概率）
y_decision_SVM = SVM_model.decision_function(X)
print("\n支持向量机模型决策函数值:")
for i, val in enumerate(y_decision_SVM):
    print(f"样本{i+1}: {val:.4f}")

# 预测测试集
y_pred_SVM = SVM_model.predict(X_test)
y_pred_Loistic = Loistic_model.predict(X_test)
y_pred_DecisionTree = DecisionTree_model.predict(X_test)
# 计算准确率
accuracy_SVM = accuracy_score(y_test, y_pred_SVM)
accuracy_Loistic = accuracy_score(y_test, y_pred_Loistic)
accuracy_DecisionTree = accuracy_score(y_test, y_pred_DecisionTree)
print(f"支持向量机模型准确率: {accuracy_SVM:.2f}")
print(f"对率回归模型准确率: {accuracy_Loistic:.2f}")
print(f"决策树模型准确率: {accuracy_DecisionTree:.2f}")

app = Flask(__name__)
CORS(app, resources={
    r"/predict/*": {
        "origins": "http://localhost:3000",
        "methods": ["POST", "OPTIONS"],
        "allow_headers": ["Content-Type"]
    }
})

# 类似地添加对率回归和决策树的接口
@app.route('/predict/svm', methods=['POST'])
def predict_svm():
    # 生成决策边界图
    with plot_lock:
        plt.figure(figsize=(10, 6))
        plot_decision_regions(X.values, y.values, clf=SVM_model, legend=2)
        plt.title('SVM决策边界')
    
    # 转换图像为base64
    img_buffer = BytesIO()
    plt.savefig(img_buffer, format='png')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    
    # 获取预测结果
    predictions = SVM_model.predict(X).tolist()
    probabilities = SVM_model.decision_function(X).tolist()  # 获取决策函数值，类似概率
    return jsonify({
        'probabilities' : probabilities,
        'predictions': predictions,
        'decision_boundary': "data:image/png;base64," + img_base64
    })

@app.route('/predict/logistic', methods=['POST'])
def predict_logistic():
    with plot_lock:
        plt.figure(figsize=(10, 6))
        plot_decision_regions(X.values, y.values, clf=Loistic_model, legend=2)
        plt.title('Logistic回归决策边界')
    
    img_buffer = BytesIO()
    plt.savefig(img_buffer, format='png')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    
    predictions = Loistic_model.predict(X).tolist()
    # 获取预测概率
    probabilities = Loistic_model.predict_proba(X)[:, 1].tolist()
    
    
    return jsonify({
        'predictions': predictions,
        'probabilities': probabilities,  # 添加预测概率
        'decision_boundary': "data:image/png;base64," + img_base64
    })

@app.route('/predict/decisiontree', methods=['POST'])
def predict_decisiontree():

    with plot_lock:
        plt.figure(figsize=(10, 6))
        plot_decision_regions(X.values, y.values, clf=DecisionTree_model, legend=2)
        plt.title('决策树决策边界')
    
    img_buffer = BytesIO()
    plt.savefig(img_buffer, format='png')
    img_buffer.seek(0)
    img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
    plt.close()
    
    with plot_lock:
        plt.figure(figsize=(15, 10))
        plot_tree(DecisionTree_model, filled=True, feature_names=X.columns, class_names=['0', '1'])
        plt.title('决策树结构')

    img_buffer_ = BytesIO()
    plt.savefig(img_buffer_, format='png')
    img_buffer_.seek(0)
    img_base64_ = base64.b64encode(img_buffer_.getvalue()).decode()
    plt.close()

    predictions = DecisionTree_model.predict(X).tolist()
    
    return jsonify({
        'predictions': predictions,
        'decision_boundary': "data:image/png;base64," + img_base64,
        'tree_structure': "data:image/png;base64," + img_base64_
    })

if __name__ == '__main__':
    app.run(port=5000)